package cn.school.course.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DateUtil;
import cn.school.common.exception.ApiException;
import cn.school.common.utils.CourseUtils;
import cn.school.common.utils.WeekDayVo;
import cn.school.course.dao.CoursePlanConflictDao;
import cn.school.course.dao.CoursePlanDao;
import cn.school.course.dao.CourseSessionsDao;
import cn.school.course.entity.Course;
import cn.school.course.entity.CoursePlan;
import cn.school.course.entity.CoursePlanConflict;
import cn.school.course.entity.CourseSessions;
import cn.school.course.service.CoursePlanConflictService;
import cn.school.course.service.CoursePlanService;
import cn.school.course.service.CourseService;
import cn.school.sys.dao.TermTimeDao;
import cn.school.sys.entity.SysConfig;
import cn.school.sys.entity.TermTime;
import cn.school.sys.service.SysConfigService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @className: 排课计划ServiceImpl
 * @author: fangjin.tao
 * @date: 2023/1/4
 **/
@Service
public class CoursePlanServiceImpl extends ServiceImpl<CoursePlanDao, CoursePlan> implements CoursePlanService {
	@Autowired
	private CoursePlanConflictService coursePlanConflictService;
	@Autowired
	private CoursePlanConflictDao coursePlanConflictDao;
	@Autowired
	private TermTimeDao termTimeDao;
	@Autowired
	private CourseSessionsDao courseSessionsDao;
	@Autowired
	private SysConfigService sysConfigService;
	@Autowired
	private CourseService courseService;
	
	
	@Override
	@Transactional
	public void saveOrUpdateBiz(CoursePlan coursePlan) {
		LambdaQueryWrapper<SysConfig> sysConfigCourseWrapper = new LambdaQueryWrapper<>();
		sysConfigCourseWrapper.eq(SysConfig::getParamKey, "t_course_plan_enable_conflict");
		List<SysConfig> tempList = sysConfigService.list(sysConfigCourseWrapper);
		SysConfig coplConflictEnable = tempList.get(0); // 排课计划-冲突策略
		
		// 是否更新
		boolean isUpdate = false;
		CoursePlan courseDb = null;
		if (coursePlan.getId() != null) {
			courseDb = this.baseMapper.selectById(coursePlan.getId());
			if (courseDb == null) {
				throw new ApiException("更新的排课计划不存在");
			}
		}
		// 保存或更新排课计划
		this.saveOrUpdate(coursePlan, isUpdate);
		// 生成校验数据
		List<CoursePlanConflict> coursePlanConflictList = this.gennerCheckData(coursePlan);
		this.coursePlanConflictService.saveBatch(coursePlanConflictList);
		// 校验冲突数据
		if ("0".equals(coplConflictEnable.getParamValue())) {
			this.verificationData(coursePlan, isUpdate, coursePlanConflictList);
		}
		
	}
	
	@Override
	@Transactional
	public void deleteCoursePlan(Long coplId) {
		CoursePlan coursePlan = this.getById(coplId);
		if (coursePlan == null) {
			return;
		}
		// 获取排课计划相关的课程
		LambdaQueryWrapper<Course> queryWrapper = new LambdaQueryWrapper<>();
		queryWrapper.eq(Course::getCoplId, coplId);
		List<Course> courseList = this.courseService.list(queryWrapper);
		if (courseList != null && courseList.size() != 0) {
			for (Course course : courseList) {
				if (course.getCreateTime().getTime() == course.getModifyTime().getTime()) {
					this.courseService.deleteCourse(course.getId());
				}
			}
		} else {
			LambdaQueryWrapper<CoursePlanConflict> lambdaQueryWrapper = new LambdaQueryWrapper<>();
			lambdaQueryWrapper.eq(CoursePlanConflict::getCoplId, coplId);
			lambdaQueryWrapper.eq(CoursePlanConflict::getCpcoIsGennerCourse, 0);
			this.coursePlanConflictService.remove(lambdaQueryWrapper);
		}
		// 删除排课计划
		this.baseMapper.deleteById(coplId);
	}
	
	/**
	 * 保存或更新排课计划
	 *
	 * @throws
	 * @author fangjin.tao
	 * @param: coursePlan
	 * @param: isUpdate
	 * @updateTime 2023/1/6 15:03
	 */
	private void saveOrUpdate(CoursePlan coursePlan, boolean isUpdate) {
		if (!isUpdate) {
			coursePlan.setCoplCourseGennerStatus("0");
			coursePlan.setCoplCourseGennerWeekNo(0);
			this.save(coursePlan);
		} else {
			this.updateById(coursePlan);
		}
	}
	
	/**
	 * 生成校验数据
	 *
	 * @throws
	 * @author fangjin.tao
	 * @param: coursePlan
	 * @param: isUpdate
	 * @updateTime 2023/1/6 15:03
	 */
	private List<CoursePlanConflict> gennerCheckData(CoursePlan coursePlan) {
		// 节次Map处理
		LambdaQueryWrapper<CourseSessions> coseQueryWrapper = new LambdaQueryWrapper<>();
		List<CourseSessions> courseSessionsList = this.courseSessionsDao.selectList(coseQueryWrapper);
		Map<Long, CourseSessions> courseSessionsMap = courseSessionsList.stream().collect(Collectors.toMap(CourseSessions::getId, obj -> obj));
		// 然后，生成自己的校验排课数据
		TermTime termTime = this.termTimeDao.selectById(coursePlan.getTetiId());
		List<Integer> weekDayNoList = coursePlan.getCoplWeekDayNoList().stream().map(weekday -> Integer.parseInt(weekday)).collect(Collectors.toList());
		List<CoursePlanConflict> gennerCoursePlanConflictList = new ArrayList<>();
		for (Integer weekDayNo : weekDayNoList) {
			List<CoursePlanConflict> tempList = this.gennerOneWeekDay(termTime, weekDayNo, coursePlan, courseSessionsMap);
			if (tempList != null && tempList.size() != 0) {
				gennerCoursePlanConflictList.addAll(tempList);
			}
		}
		return gennerCoursePlanConflictList;
	}
	
	/**
	 * 生成某个星期几的课
	 *
	 * @throws
	 * @author fangjin.tao
	 * @param: termTime 学年学期
	 * @param: weekDay 星期几
	 * @param: coursePlan 排课计划
	 * @param: courseSessionsMap 节次Map
	 * @updateTime 2023/2/2 16:12
	 * @return: java.util.List<cn.school.course.entity.CoursePlanConflict>
	 */
	private List<CoursePlanConflict> gennerOneWeekDay(TermTime termTime, Integer weekDay, CoursePlan coursePlan, Map<Long, CourseSessions> courseSessionsMap) {
		WeekDayVo beginBeginWeedDayVo = CourseUtils.gennerWeedDayDate(termTime.getTetiTermBeginDate(),
				coursePlan.getCoplBeginWeekNo(), weekDay);
		int beginWeek = coursePlan.getCoplBeginWeekNo();
		int endWeek = coursePlan.getCoplEndWeekNo();
		
		List<CoursePlanConflict> gennerCoursePlanConflictList = new ArrayList<>();
		CoursePlanConflict tempCourseplanConflict = null;
		while (true) {
			if (beginWeek > endWeek ||
					DateUtil.beginOfDay(beginBeginWeedDayVo.getDate()).getTime() > DateUtil.beginOfDay(termTime.getTetiTermEndDate()).getTime()) {
				break;
			}
			if (("d".equals(coursePlan.getCoplIsDuboleWeek()) && (beginWeek % 2 == 0)) ||
					("s".equals(coursePlan.getCoplIsDuboleWeek()) && (beginWeek % 2 != 0)) ||
					((!"d".equals(coursePlan.getCoplIsDuboleWeek())) && (!"s".equals(coursePlan.getCoplIsDuboleWeek())))) {
				tempCourseplanConflict = new CoursePlanConflict();
				BeanUtil.copyProperties(coursePlan, tempCourseplanConflict);
				tempCourseplanConflict.setCoplWeekDayNo(weekDay);
				tempCourseplanConflict.setCoplId(coursePlan.getId());
				tempCourseplanConflict.setCoplWeekNo(beginWeek);
				tempCourseplanConflict.setCpcoIsChanged(0);
				tempCourseplanConflict.setCpcoIsConflict(0);
				tempCourseplanConflict.setCpcoTeacherKey(coursePlan.getTetiId() + "_" + coursePlan.getSyusId() + "_"
						+ beginWeek + "_" + coursePlan.getCoseId() + "_" + weekDay);
				tempCourseplanConflict.setCpcoRoomKey(coursePlan.getTetiId() + "_" + coursePlan.getBsloId() + "_"
						+ beginWeek + "_" + coursePlan.getCoseId() + "_" + weekDay);
				tempCourseplanConflict.setCourBeginTime(
						DateUtil.parseDateTime(
								DateUtil.format(beginBeginWeedDayVo.getDate(), "yyyy-MM-dd") + " " + courseSessionsMap.get(coursePlan.getCoseId()).getCoseBeginTime()
						)
				);
				tempCourseplanConflict.setCourEndTime(DateUtil.parseDateTime(
						DateUtil.format(beginBeginWeedDayVo.getDate(), "yyyy-MM-dd") + " " + courseSessionsMap.get(coursePlan.getCoseId()).getCoseEndTime()
				));
				if (tempCourseplanConflict.getCourEndTime().getTime() > Calendar.getInstance().getTime().getTime()) {
					gennerCoursePlanConflictList.add(tempCourseplanConflict);
				}
			}
			
			beginWeek++;
			beginBeginWeedDayVo = CourseUtils.gennerWeedDayDate(termTime.getTetiTermBeginDate(),
					beginWeek, weekDay);
		}
		
		return gennerCoursePlanConflictList;
	}
	
	/**
	 * 生成校验数据
	 *
	 * @throws
	 * @author fangjin.tao
	 * @param: coursePlan
	 * @param: isUpdate
	 * @updateTime 2023/1/6 15:04
	 */
	private void verificationData(CoursePlan coursePlan, boolean isUpdate, List<CoursePlanConflict> coursePlanConflictList) {
		if (isUpdate) {
			// 首先，删除当前自己的校验排课数据(但不包括已生成单节课的课程)
			LambdaQueryWrapper<CoursePlanConflict> planConflictWrapper = new LambdaQueryWrapper<>();
			planConflictWrapper.eq(CoursePlanConflict::getCoplId, coursePlan.getId());
			planConflictWrapper.eq(CoursePlanConflict::getCpcoIsGennerCourse, 0); // 未生成单节课
			List<CoursePlanConflict> coursePlanConflict = this.coursePlanConflictDao.selectList(planConflictWrapper);
			if (coursePlanConflict != null && coursePlanConflict.size() != 0) {
				this.coursePlanConflictDao.deleteBatchIds(coursePlanConflict.stream().map(CoursePlanConflict::getId).collect(Collectors.toList()));
			}
		}
		if (coursePlanConflictList != null && coursePlanConflictList.size() != 0) {
			List<String> teahcerKeyList = new ArrayList<>();
			List<String> roomKeyList = new ArrayList<>();
			for (CoursePlanConflict coursePlanConflict : coursePlanConflictList) {
				teahcerKeyList.add(coursePlanConflict.getCpcoTeacherKey());
				roomKeyList.add(coursePlanConflict.getCpcoRoomKey());
			}
			LambdaQueryWrapper<CoursePlanConflict> queryWrapper = new LambdaQueryWrapper<>();
			queryWrapper.ne(CoursePlanConflict::getCoplId, coursePlan.getId());
			queryWrapper.and((wrapper) -> {
				wrapper.in(CoursePlanConflict::getCpcoTeacherKey, teahcerKeyList)
						.or()
						.in(CoursePlanConflict::getCpcoRoomKey, roomKeyList);
			});
			int count = this.coursePlanConflictDao.selectCount(queryWrapper);
			if (count > 0) {
				throw new ApiException("排课计划对应的老师课程分身冲突，或对应场地占用");
			}
		}
		
	}
}
